Now
it's time to talk about how Dudel is going to draw. Instead of dealing
with an underlying pixel-buffer canvas on which all operations are
performed, we're going to maintain a list of drawing operations,
defined by the user's actions. Each object that the user draws will be
added to an array, and each of the objects in the array will be drawn
when necessary.
For maximum modularity, let's decide that each drawing operation should know how to draw itself. Then all our DudelView
class really needs to do is hang onto an array and pass along a draw
request whenever it's time to redraw. We'll try to maintain some sort
of order here by defining a protocol called Drawable, which contains a single method called draw.
Any object that represents a drawing operation should conform to this
protocol. Also, there will be times—such as while the user is creating
a drawing operation by dragging a finger around the screen—that some
temporary drawing will need to be done. The view class won't be
responsible for this, however. We'll pass that to our delegate object,
the DudelViewController. So, we'll add a method to the DudelViewDelegate method, giving the controller a chance to do some temporary, context-based drawing.
Create a new protocol
header file (one of the file types found in the Cocoa Touch Class
section of the New File Assistant) in your project, name it Drawable.h, and give it the following content:
// Drawable.h
@protocol Drawable
- (void)draw;
@end
Now we're ready to flesh out the DudelView class. First, add a few lines to DudelView.h:
// DudelView.h
#import <UIKit/UIKit.h>
@protocol DudelViewDelegate
- (void)drawTemporary;
@end
@interface DudelView : UIView {
NSMutableArray *drawables;
IBOutlet id <DudelViewDelegate> delegate;
}
@property (retain, nonatomic) NSMutableArray *drawables;
@end
Then define the DudelView.m file as follows:
// DudelView.m
#import "DudelView.h"
#import "Drawable.h"
@implementation DudelView
@synthesize drawables;
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
drawables = [[NSMutableArray alloc] initWithCapacity:100];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
drawables = [[NSMutableArray alloc] initWithCapacity:100];
}
return self;
}
- (void)drawRect:(CGRect)rect {
for (<Drawable> d in drawables) {
[d draw];
}
[delegate drawTemporary];
}
- (void)dealloc {
[drawables release];
[super dealloc];
}
@end
You'll notice
that we define two different initialization methods. The first is
normally called within code; the second is called when an object is
being instantiated from a nib file. In our current implementation, only
the nib file version is being used, but we may as well cover the other
possibility as well.
Apart from that, this code is
quite straightforward. Other objects can directly add drawable display
operations to the view's array. Whenever drawRect: is called (as a result of someone, somewhere, calling setNeedsDisplay on the view), the view just calls draw everywhere else.